/**
 * @file battery_test_i2c.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-11-26
 *  i2c and smbus api is different
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>


#define I2C_SLAVE_ADDR (0x16)

//char dev driver
#define CLASS_NAME            "testi2c"
#define DEVICE_NAME           "testi2c_dev"
#define I2C_TRY_COUNT         (3)

static int test_int = 99;

module_param_named(test_int_show, test_int, int, 0644);

typedef struct testi2c_struct
{
    struct device *dev;
    struct i2c_client *client;
    struct device_node *node; 
    struct i2c_adapter *adapter;

    struct class *class;
    struct device *device;
    int major;

    struct device_attribute *dev_attr;
    struct attribute *attr[];
    
}testi2c_struct_t;

//sysfs 
static ssize_t test_show_1(struct device *dev,
                struct device_attribute *attr, char *buf)
{
      printk("is sysfs test\n");
      return 0;
}

static DEVICE_ATTR(test_show, S_IRUGO, test_show_1, NULL);
static struct attribute *testi2c_atts[] = {
    &dev_attr_test_show.attr,
    NULL
};

static const struct attribute_group testi2c_group = {
    .attrs = testi2c_atts
};

static int tsensor_read_reg(struct i2c_client *client, u8 reg)
{
    int try = I2C_TRY_COUNT;
    int ret;

    do {
        ret = i2c_smbus_read_byte_data(client, reg);
    } while ((ret == -ENXIO || ret == -EIO) && --try);
    if (ret < 0) {
        pr_err("Unable to read register 0x%02x\n", reg);
    }

    return ret;
}

static int testi2c_read_regs(struct i2c_client *client, u8 reg, u8 count,
                 u8 *values)
{
    int try = I2C_TRY_COUNT;
    int ret;

    do {
        ret = i2c_smbus_read_i2c_block_data(client, reg, count, values);
    } while ((ret == -ENXIO || ret == -EIO) && --try);
    if (ret != count) {
        pr_err("Unable to read registers 0x%02x..0x%02x\n", reg, reg + count - 1);
        return ret < 0 ? ret : -EIO;
    }

    return 0;
}

static int testi2c_write_reg(struct i2c_client *client, u8 reg, u8 value)
{
    int try = I2C_TRY_COUNT;
    int ret;

    do {
        ret = i2c_smbus_write_byte_data(client, reg, value);
    } while ((ret == -ENXIO || ret == -EIO) && --try);
    if (ret) {
        pr_err("Unable to write register 0x%02x\n", reg);
    }

    return ret;
}

static int testi2c_write_regs(struct i2c_client *client, u8 reg, u8 count,
                  const u8 *values)
{
    int try = I2C_TRY_COUNT;
    int ret;

    do {
        ret = i2c_smbus_write_i2c_block_data(client, reg, count, values);
    } while ((ret == -ENXIO || ret == -EIO) && --try);
    if (ret) {
        pr_err("Unable to write registers 0x%02x..0x%02x\n", reg, reg + count - 1);
    }

    return ret;
}

static int tsensor_open(struct inode *node, struct file *file)
{
    pr_debug("tsensor_open \n");
    return 0;
}

static ssize_t tsensor_read(struct file *file, char *buf, size_t len,
                loff_t *offset)
{
    pr_debug("tsensor_read \n");

    return 0;
}

static ssize_t tsensor_write(struct file *file, const char *buf, size_t len,
                 loff_t *offset)
{
    pr_debug("tsensor_write \n");
    return 0;
}

static int tsensor_release(struct inode *node, struct file *file)
{
    pr_debug("tsensor_release\n");
    return 0;
}

static struct file_operations tsensor_fops = {
    .owner = THIS_MODULE,
    .open = tsensor_open,
    .read = tsensor_read,
    .write = tsensor_write,
    .release = tsensor_release,
};


static int user_init(testi2c_struct_t *test_i2c)
{
    int ret;

    pr_debug("%s enter.\n", __func__);

    int major = register_chrdev(0, DEVICE_NAME, &tsensor_fops);
    if (major < 0) {
        pr_err("Register chrdev failed.\n");
        return major;
    }
    pr_debug("Register success, major number is %d\n", major);
    test_i2c->major = major;

    test_i2c->class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(test_i2c->class)) {
        unregister_chrdev(major, DEVICE_NAME);
        pr_warn("Unable to create tsensor class, errno = %ld\n", PTR_ERR(test_i2c->class));
        return PTR_ERR(test_i2c->class);
    }

    test_i2c->device = device_create(test_i2c->class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
    if (IS_ERR(test_i2c->device)) {
        class_destroy(test_i2c->class);
        unregister_chrdev(major, DEVICE_NAME);
        pr_warn("Unable to create tsensor dev, errno = %ld\n", PTR_ERR(test_i2c->device));
        return PTR_ERR(test_i2c->device);
    }
    pr_debug("tsensor device init success.\n");

    ret = sysfs_create_group(&test_i2c->device->kobj, &testi2c_group);
    if (ret != 0) {
        device_destroy(test_i2c->class, MKDEV(major, 0));
        class_destroy(test_i2c->class);
        unregister_chrdev(major, DEVICE_NAME);
        pr_err("sysfs create file fail.\n");
        return ret;
    }

    return 0;
}

static int testi2c_probe(struct i2c_client *client,
             const struct i2c_device_id *id)
{  
    const u32 test_u8[3] = {0};
    const char **buffer = NULL;
    int ret;
    printk("is probe\n");
    //test i2c
    testi2c_struct_t *test_i2c = devm_kzalloc(&client->dev, sizeof(testi2c_struct_t), GFP_KERNEL);
    if (test_i2c == NULL) 
    {
        return -ENOMEM;
    }

    test_i2c->adapter = client->adapter;
    test_i2c->client = client;
    test_i2c->dev = &client->dev;
    test_i2c->node =client->dev.parent->of_node;

    if (!i2c_check_functionality(test_i2c->adapter,
                I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) 
    {
        pr_err("doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n");
        return -EIO;
    }

    buffer = kcalloc(2, sizeof(char *), GFP_KERNEL);
    if(buffer == NULL)
    {
        return -ENOMEM;
    }

    //user init
    user_init(test_i2c);

    i2c_set_clientdata(client, test_i2c);

    //test device tree read
    int int_count = device_property_count_u32(&client->dev, "user_param");
    printk("int_count %d\n", int_count);
    device_property_read_u32_array(&client->dev, "user_param", test_u8, 1);
    printk("test_u8 %d %d\n", test_u8[0], test_u8[1]);
    int str_count =  device_property_string_array_count(&client->dev, "user_param_str");
    printk("str_count %d\n", str_count);
    device_property_read_string_array(&client->dev, "user_param_str", buffer, 2);
    printk("buffer: %s %s\n", buffer[0], buffer[1]);
    extern int test_iio_init(struct device *dev);
    ret = test_iio_init(&client->dev);
    printk("iio init: %d\n", ret);
    return 0;
}

static int testi2c_remove(struct i2c_client *client)
{
    printk("is remove\n");
    testi2c_struct_t *test_i2c = i2c_get_clientdata(client);
    sysfs_remove_group(&test_i2c->device->kobj, &testi2c_group);
    device_destroy(test_i2c->class, MKDEV(test_i2c->major, 0));
    class_destroy(test_i2c->class);
    unregister_chrdev(test_i2c->major, DEVICE_NAME);
    return 0;
}

static int __maybe_unused tsensor_suspend(struct device *dev)
{
    printk("battery test i2c sleep \n");
    return 0;
}

static int __maybe_unused tsensor_resume(struct device *dev)
{
    printk("battery test i2c wakeup \n");
    return 0;
}

static SIMPLE_DEV_PM_OPS(testi2c_pm_ops, tsensor_suspend, tsensor_resume);

static const struct of_device_id testi2c_of_match[] = {
    { .compatible = "test,testi2c" },
    {}
};

static const struct i2c_device_id testi2c_id[] = {
    { "test,testi2c", 0 },
    {}
};
MODULE_DEVICE_TABLE(i2c, testi2c_id);

static struct i2c_driver testi2c_driver = {
    .driver = {
        .name = "test,testi2c",
        .owner = THIS_MODULE,
        .of_match_table = testi2c_of_match,
        .pm = &testi2c_pm_ops,
    },
    .probe = testi2c_probe,
    .remove = testi2c_remove,
    .id_table = testi2c_id,
};

module_i2c_driver(testi2c_driver);

MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
MODULE_LICENSE("GPL");

 
